﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SafeCalculations.SafeTypes;
using System.Numerics;
using System.Diagnostics;

namespace SafeCalculations.UnitTests
{
    [TestClass]
    public class Int32WithBoundedRolloverTests
    {
#if RUN_PERFORMANCE_TESTS
        /// <summary>
        /// Meant as a quick performance test for comparing the summation of integers without overflow provided in this project to the same calculation using only BigIntegers.
        /// </summary>
        [TestMethod]
        public void PerformanceTest()
        {
            Stopwatch sw = new Stopwatch();

            int[] values = Enumerable.Repeat<int>(1, 100000).ToArray();
            int numRuns = 100;

            sw.Start();

            BigInteger sum = 0;
            for (int j = 0; j < numRuns; j++)
            {
                for (int i = 0; i < values.Length; i++)
                    sum += values[i];
            }

            sw.Stop();

            var elapsedTimeSimpleMethod = sw.ElapsedMilliseconds;

            sw.Reset();

            sw.Start();

            for (int i = 0; i < numRuns; i++)
            { Int32WithBoundedRollover int32 = new Int32WithBoundedRollover(values); }

            sw.Stop();

            var elapsedTimeSafeCalculationsMethod = sw.ElapsedMilliseconds;

            //To view performance results I normally run in debug mode and set a break point
        }
#endif

        [TestMethod]
        public void VerifyBasicInt32Operation()
        {
            int[] testValues = new int[] { 1, 2, 3 };

            Int32WithBoundedRollover val = new Int32WithBoundedRollover(testValues);
            Assert.AreEqual(testValues.Sum(), val.ToInt32());
        }

        [TestMethod]
        public void VerifySinglePositiveOverflowValueSmallRemainder()
        { this.AssertCorrectSumForValuesUsingBigIntegers(new int[] { int.MaxValue, 2 }); }

        [TestMethod]
        public void VerifySinglePositiveOverflowValueLargeRemainder()
        { this.AssertCorrectSumForValuesUsingBigIntegers(new int[] { int.MaxValue, int.MaxValue, int.MaxValue / 2 }); }

        [TestMethod]
        public void VerifySingleNegativeOverflowValueSmallRemainder()
        { this.AssertCorrectSumForValuesUsingBigIntegers(new int[] { int.MinValue, -2 }); }

        [TestMethod]
        public void VerifySingleNegativeOverflowValueLargeRemainder()
        { this.AssertCorrectSumForValuesUsingBigIntegers(new int[] { int.MinValue, int.MinValue, int.MinValue / 2 }); }

        [TestMethod]
        public void VerifyMultipleFullPositiveOverflowValue()
        { this.AssertCorrectSumForValuesUsingBigIntegers(Enumerable.Repeat<int>(int.MaxValue, 10)); }

        [TestMethod]
        public void VerifyMultipleFullNegativeOverflowValue()
        { this.AssertCorrectSumForValuesUsingBigIntegers(Enumerable.Repeat<int>(int.MinValue, 10)); }

        [TestMethod]
        public void VerifyPositiveOverflowThenBackToNoOverflowValue()
        { this.AssertCorrectSumForValuesUsingBigIntegers(new int[] { int.MaxValue, 10, -100 }); }

        [TestMethod]
        public void VerifyNegativeOverflowThenBackToNoOverflowValue()
        { this.AssertCorrectSumForValuesUsingBigIntegers(new int[] { int.MinValue, -10, 100 }); }

        [TestMethod]
        public void VerifySingleNegativeOverflowAndRemainderEqualsZero()
        { this.AssertCorrectSumForValuesUsingBigIntegers(new int[] { int.MinValue, -1, int.MinValue }); }

        [TestMethod]
        public void VerifySingleNegativeOverflowAndRemainderEqualsNegativeOne()
        { this.AssertCorrectSumForValuesUsingBigIntegers(new int[] { int.MinValue, -1, (int.MinValue + 1) }); }

        [TestMethod]
        public void VerifySingleNegativeOverflowAndRemainderEqualsNegativeTwo()
        { this.AssertCorrectSumForValuesUsingBigIntegers(new int[] { int.MinValue, -1, (int.MinValue + 2) }); }

        [TestMethod]
        public void VerifySinglePositiveOverflowAndRemainderEqualsZero()
        { this.AssertCorrectSumForValuesUsingBigIntegers(new int[] { int.MaxValue, 1, int.MaxValue, 1 }); }

        [TestMethod]
        public void VerifySinglePositiveOverflowAndRemainderEqualsNegativeOne()
        { this.AssertCorrectSumForValuesUsingBigIntegers(new int[] { int.MaxValue, 1, int.MaxValue }); }

        [TestMethod]
        public void VerifySinglePositiveOverflowAndRemainderEqualsNegativeTwo()
        { this.AssertCorrectSumForValuesUsingBigIntegers(new int[] { int.MaxValue, 1, (int.MaxValue - 1) }); }

        private void AssertCorrectSumForValuesUsingBigIntegers(IEnumerable<int> values)
        { Assert.AreEqual(this.SumIntsAsBigIntegers(values), this.GetBigIntegerValueOfRolloverSum(values)); }

        private BigInteger SumIntsAsBigIntegers(IEnumerable<int> integers)
        { return integers.Select(v => new BigInteger(v)).Aggregate((x, y) => x + y); }

        private BigInteger GetBigIntegerValueOfRolloverSum(IEnumerable<int> integers)
        {
            Int32WithBoundedRollover val = new Int32WithBoundedRollover(integers.ToArray());
            return val.ToBigInteger();
        }
    }
}
